﻿using System;
using OpenCvSharp;
using Modules;
namespace ObjectDetector
{
    public class Detector
    {
        private Mat Template;
        public int ThresholdValue=200;
        private int WaitTime = 0;
        private double TemplateFillRatio ;
        private Point[] TemplateContours;
        public Detector()
        {
            // 调试trackbar 调整当前图像的轮廓检测的阈值
            Cv2.NamedWindow("image_contours");
            Cv2.CreateTrackbar("threshold", "image_contours",ref ThresholdValue,255);
        }
        public void SetTemplate(Mat image)
        {
            Template = image.Clone();
            Point[][] template_contours;
            Mat template = Template.Clone();
            
            // 二值化并绘制轮廓
            ContoursInformation(ref template, out template_contours, 150);
            var template_contours_img = new Mat(template.Rows, template.Cols, MatType.CV_8UC1, new Scalar(0, 0, 0));
            Cv2.DrawContours(template_contours_img, template_contours, -1, Scalar.White);
            
            // 查找模板的外轮廓
            int max_area_contours = 0;
            double max_area = 0;
            for (int i = 0; i < template_contours.Length; i++)
            {
                if (Cv2.ContourArea(template_contours[i]) > max_area)
                {
                    max_area = Cv2.ContourArea(template_contours[i]);
                    max_area_contours = i;
                }
            }
            // Cv2.DrawContours(template,template_contours,max_area_contours,Scalar.Red,3);
            
            // 计算轮廓的填充率
            var template_rotateRectangle = Cv2.MinAreaRect(template_contours[max_area_contours]);
            var template_bounding = template_rotateRectangle.BoundingRect();
            var target = new Mat(Template, template_bounding);
            TemplateFillRatio = Cv2.ContourArea(template_contours[max_area_contours]) / (template_rotateRectangle.Size.Height * template_rotateRectangle.Size.Width);
            TemplateContours = template_contours[max_area_contours];
            
            
            Cv2.ImShow("template_contours", template_contours_img);
            Cv2.ImShow("Template", template);
            Cv2.WaitKey(WaitTime);
        }

        public void Execute(Mat image)
        {

            Point[][] image_contours;
            Mat roi = null;
            Mat src = image.Clone();
            
            // 绘制检测图像的所有外轮廓
            ThresholdValue = Cv2.GetTrackbarPos("threshold", "image_contours");
            ContoursInformation(ref image, out image_contours, ThresholdValue);
            var image_contours_img = new Mat(image.Rows, image.Cols, MatType.CV_8UC1, new Scalar(0, 0, 0));
            Cv2.DrawContours(image_contours_img, image_contours, -1, Scalar.White);
            
            // 遍历所有轮廓和模板轮廓进行匹配
            int min_pos = 0;
            double min_diif = 0x7ffffff;
            for (int i = 0; i < image_contours.Length; i++)
            {
                if (Cv2.ContourArea(image_contours[i]) > 1000)
                {
                    double diff_score = Cv2.MatchShapes(image_contours[i], TemplateContours, ShapeMatchModes.I3);
                    if (diff_score < 0.5)
                    {
                        min_diif = diff_score;
                        min_pos = i;

                        var rectangle = Cv2.MinAreaRect(image_contours[i]);

                        double fill_ratio = Cv2.ContourArea(image_contours[i]) / (rectangle.Size.Height * rectangle.Size.Width);

                        // Console.Write(rectangle.Angle);
                        if (Math.Abs(TemplateFillRatio - fill_ratio) > 0.1) continue;
                        Cv2.DrawContours(image, image_contours, min_pos, Scalar.Red, 3);
                        var image_bounding = rectangle.BoundingRect();
                        
                        // todo 二次校验
                        // Cv2.Rectangle(image,image_bounding,Scalar.Blue,2);
                        // roi = new Mat(image,image_bounding);
                        // Mat result = OnlyTargetMat(image, image_contours, i, image_bounding);
                        // Mat result_target = OnlyTargetMat(template, template_contours, max_area_contours,
                        // template_bounding);

                        // DoubleFeatureVerify(result, result_target,0.9);
                        // Cv2.ImShow("roi",result_target);
                        // Cv2.WaitKey(0);
                        // 输出归一化的角度 范围在0~180
                        var norm_rectangle = Modules.GeometryFeatureModule.StandardizeRotatedRectangle(rectangle);
                        Console.Write("{0:f} {1:f}  ", norm_rectangle.Angle, fill_ratio);
                        Console.Write("X:{0:D} Y:{1:D} ",(int)(norm_rectangle.Center.X),(int)(norm_rectangle.Center.Y));
                        // 代表当前轮廓和目标的差异越小越好
                        Console.WriteLine(min_diif);

                    }
                }
            }


            Mat show_image_contours = image_contours_img.Resize((new Size(720, 540)));
            Cv2.ImShow("image_contours", show_image_contours);
            Mat show_image = image.Resize(new Size(720, 540));
            Cv2.ImShow("image", show_image);
            Cv2.WaitKey(WaitTime);

        }

        private void ContoursInformation(ref Mat image,out Point[][] contours,int value)
        {
            var gray = new Mat();
            var binary = new Mat();
            HierarchyIndex[] hierarchy;
            
            Cv2.CvtColor(image,gray,ColorConversionCodes.BGR2GRAY);
            Cv2.Threshold(gray, binary, value, 255, ThresholdTypes.Binary );
            // todo 自适应阈值效
            // Cv2.Threshold(gray, binary, value, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);

            Mat element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(5, 5));
            Cv2.MorphologyEx(binary,binary,MorphTypes.Open,element,new Point(-1,-1),1);
            Cv2.MorphologyEx(binary,binary,MorphTypes.Close,element,new Point(-1,-1),1);
            Cv2.FindContours(binary, out contours,out hierarchy, RetrievalModes.External,ContourApproximationModes.ApproxSimple);
            if(image.Width>image.Height)Cv2.ImShow("binary",binary);

        }

    }
}